﻿/*
 * Created by SharpDevelop.
 * User: yeang-shing.then
 * Date: 11/2/2012
 * Time: 1:59 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Text;

namespace KCLIMTrading.Mgmt
{
	/// <summary>
	/// Database object.
	/// </summary>
	public class Database
	{
		private DataTable productTable;
		/// <summary>
		/// Gets or sets product list for database.
		/// </summary>
		public SortableBindingList<Product> Products { get; set; }
		/// <summary>
		/// Gets or sets collection of purchase objects.
		/// </summary>
		public List<PurchaseOrder> PurchaseOrders { get; set; }

		public Database()
		{
			this.Products = new SortableBindingList<Product>();
			this.PurchaseOrders = new List<PurchaseOrder>();
			this.productTable = CreateProductTable();

			Load();
		}
		private void Load()
		{
			//load product table
			//TODO: handle file not exist
			string file = AppDomain.CurrentDomain.BaseDirectory
				+ System.IO.Path.DirectorySeparatorChar + "Data"
				+ System.IO.Path.DirectorySeparatorChar + "Products.xml";
			System.Diagnostics.Debug.WriteLine(file);
			DataSet dataSet = new DataSet();
			dataSet.ReadXml(file);
			foreach (DataRow row in dataSet.Tables[0].Rows)
			{
				Product product = new Product();
				product.Code = row["Code"].ToString();
				product.Name = row["Name"].ToString();
				product.Price = Convert.ToDecimal(row["Price"]);
				this.Products.Add(product);
			}

			//TODO: continue load customer
			
			//load collection of PO descending. The latest appear fist line in DataGrid
			DirectoryInfo directoryInfo = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory + "Purchase Order");
			FileInfo[] fileInfos = directoryInfo.GetFiles();
			//ReverseSort(fileInfos);
			foreach(FileInfo fileInfo in fileInfos)
			{
				System.Diagnostics.Debug.WriteLine("loading "+fileInfo.Name);
				StreamReader reader = new StreamReader(fileInfo.FullName);
				string xml = reader.ReadToEnd();
				reader.Close();
				
				PurchaseOrder po = (PurchaseOrder)DeserializeObject(xml,typeof(PurchaseOrder));
				this.PurchaseOrders.Add(po);
			}
			
			//by default sort by DeliveryDate desc
			PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(PurchaseOrder));
			PropertyDescriptor property = properties.Find("DeliveryDate",false);
			this.PurchaseOrders.Sort(new PropertyComparer<PurchaseOrder>(property, ListSortDirection.Descending));
		}
		/// <summary>
		/// Add or update product into database.
		/// </summary>
		/// <remarks>
		/// If not found will create a new product otherwise will overwrite the existing based on the product code.
		/// </remarks>
		/// <param name="product"></param>
		public void Add(Product product)
		{
			foreach(Product p in this.Products)
			{
				if(p.Code == product.Code)
				{
					p.Name = product.Name;
					p.Price = product.Price;
					return;
				}
			}
			
			this.Products.Add(product);
		}
		public void Add(Customer customer)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Save all data into file structure at once.
		/// </summary>
		/// <returns></returns>
		public bool Save()
		{
			string file = AppDomain.CurrentDomain.BaseDirectory
				+ System.IO.Path.DirectorySeparatorChar + "Data"
				+ System.IO.Path.DirectorySeparatorChar + "Products.xml";
			System.Diagnostics.Debug.WriteLine("write to " + file);

			//save product table
			//StreamWriter writer = new StreamWriter(file);
			//String sXml = SerializeObject(typeof(Product), this.Products);
			//writer.Write(sXml);
			//System.Diagnostics.Debug.WriteLine("writer.Close()");
			//writer.Close();
			foreach (Product product in this.Products)
			{
				DataRow newRow = productTable.NewRow();
				newRow["Code"] = product.Code;
				newRow["Name"] = product.Name;
				newRow["Price"] = product.Price;
				productTable.Rows.Add(newRow);
			}
			productTable.AcceptChanges();
			DataSet dataSet = new DataSet("Products");
			dataSet.Tables.Add(productTable);
			dataSet.WriteXml(file);


			//todo: continue save other customer
			
			
			//save all po
			foreach (PurchaseOrder order in this.PurchaseOrders)
			{
				string sXml = SerializeObject(typeof(PurchaseOrder), order);
				//System.Diagnostics.Debug.WriteLine("XML: " + sXml);

				String xmlFilePath = AppDomain.CurrentDomain.BaseDirectory + "Purchase Order" + Path.DirectorySeparatorChar + order.Number + ".xml";
				StreamWriter writer = new StreamWriter(xmlFilePath);
				writer.Write(sXml);
				writer.Close();
				System.Diagnostics.Debug.WriteLine("write to " + xmlFilePath);
			}

			return true;
		}
		/// <summary>
		/// Return DataTable with same structure of Product object.
		/// </summary>
		/// <returns></returns>
		private DataTable CreateProductTable()
		{
			DataTable table = new DataTable("Product");
			table.Columns.Add(new DataColumn("Code", typeof(String)));
			table.Columns.Add(new DataColumn("Name", typeof(String)));
			table.Columns.Add(new DataColumn("Price", typeof(Decimal)));
			table.AcceptChanges();

			return table;
		}
		/// <summary>

		/// Method to reconstruct an Object from XML string.
		/// </summary>
		/// <param name="pXmlizedString"></param>
		/// <returns></returns>
		/// <seealso cref="/// http://www.dotnetjohn.com/articles.aspx?articleid=173">Deserialize</seealso>
		public Object DeserializeObject (String pXmlizedString, Type type )
		{
			XmlSerializer xs = new XmlSerializer (type);
			
			UTF8Encoding encoding = new UTF8Encoding ( );
			Byte[ ] byteArray = encoding.GetBytes ( pXmlizedString );
			MemoryStream memoryStream = new MemoryStream ( byteArray );
			XmlTextWriter xmlTextWriter = new XmlTextWriter ( memoryStream, Encoding.UTF8 );

			return xs.Deserialize ( memoryStream );
		}

		/// <summary>
		/// http://yosi-havia.blogspot.com/2009/08/how-to-transform-between-object-and-xml.html
		/// http://stackoverflow.com/questions/1123718/format-xml-string-to-print-friendly-xml-string
		/// </summary>
		/// <param name="type"></param>
		/// <param name="obj"></param>
		/// <returns></returns>
		public String SerializeObject(Type type, object obj)
		{
			MemoryStream memory = null;
			XmlTextWriter textWriter = null;
			String sXml = null;

			try
			{
				memory = new MemoryStream();
				XmlSerializer serializer = new XmlSerializer(type);
				textWriter = new XmlTextWriter(memory, Encoding.UTF8);
				textWriter.Formatting = Formatting.Indented;//key

				serializer.Serialize(textWriter, obj);

				memory = (MemoryStream)textWriter.BaseStream;
				UTF8Encoding oUTF8Encoding = new UTF8Encoding();
				sXml = oUTF8Encoding.GetString(memory.ToArray());
				//System.Diagnostics.Debug.WriteLine(sXml);
			}
			catch (Exception ex)
			{
				System.Diagnostics.Debug.WriteLine(ex.Message);
			}
			finally
			{
				if (memory != null) memory.Close();
				if (textWriter != null && textWriter.WriteState != WriteState.Closed)
					textWriter.Close();
			}

			return sXml;
		}
		
		/// <summary>
		/// Sort the items of an array using bubble sort.
		/// </summary>
		/// <param name="ar"></param>
		public void ReverseSort(FileInfo[] ar)
		{
			for(int pass=0; pass<ar.Length; pass++)
			{
				for(int i=0; i<ar.Length - 1; i++)
				{
					if(ar[i].FullName.CompareTo(ar[i+1].FullName) < 0)
						Swap(ar, i);
				}
			}
		}
		/// <summary>
		/// Swap two items of an array.
		/// </summary>
		/// <param name="ar"></param>
		/// <param name="first"></param>
		private void Swap(FileInfo[] ar, int first)
		{
			FileInfo hold = ar[first];
			ar[first] = ar[first+1];
			ar[first+1] = hold;
		}
	}
}